/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.bomt.service.impl;

import com.comac.ins.bomt.common.enums.BizFileTypeEnum;
import com.comac.ins.bomt.common.enums.BomDetailTypeEnum;
import com.comac.ins.bomt.domain.*;
import com.comac.ins.bomt.domain.vo.*;
import com.comac.ins.bomt.service.BizFileService;
import com.comac.ins.bomt.service.BomExportService;
import com.comac.ins.bomt.service.BomQueryService;
import com.comac.ins.common.core.domain.model.LoginUser;
import com.comac.ins.common.biz.exception.BizException;
import com.comac.ins.common.biz.utils.BizAssertUtil;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.excel.utils.ExcelUtil;
import com.comac.ins.common.json.utils.JsonUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.comac.ins.bomt.common.enums.StrengthChangeStatisticTypeEnum.ADD;

/**
 * Bom导出服务
 *
 * @author zhanghongyi
 * @date 2024/8/23
 * @description BomExportServiceImpl
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class BomExportServiceImpl implements BomExportService {

    private final BizFileService bizFileService;

    private final BomQueryService bomQueryService;

    @Override
    public Long exportStandardBom(LoginUser loginUser, BomInfo bomInfo, Path exportDir) {
        List<DetailStandardBom> standardDetails = bomQueryService.queryStandardBomDetailsByBomInfo(bomInfo);
        return standardBomExport(loginUser, MapstructUtils.convert(standardDetails, DetailStandardBomVo.class), bomInfo.getAircraftModel(), bomInfo.getTrack(), bomInfo.getGroupType(), exportDir);
    }

    @Override
    public Long exportStrengthBom(LoginUser loginUser, BomInfo bomInfo, Path exportDir) {
        List<DetailStrengthBom> strengthDetails = bomQueryService.queryStrengthBomDetailsByBomInfo(bomInfo);
        return strengthBomExport(loginUser, MapstructUtils.convert(strengthDetails, DetailStrengthBomVo.class), bomInfo.getAircraftModel(), bomInfo.getTrack(), bomInfo.getGroupType(), exportDir);
    }

    @Override
    public Long exportStrengthStandardCompare(LoginUser loginUser, BomInfo bomInfo, Path exportDir) {
        List<DetailStrengthStandardChange> strengthStandardChanges = bomQueryService.queryStrengthStandardChangeDetailsByBomInfo(bomInfo);
        SummaryStrengthStandardChangeVo summary = JsonUtils.parseObject(bomInfo.getStrengthVsStandardSummary(), SummaryStrengthStandardChangeVo.class);
        BizAssertUtil.notNull(summary, "parse change summary fail");
        summary.setChangeDetailList(MapstructUtils.convert(strengthStandardChanges, DetailStrengthStandardChangeVo.class));
        return strengthStandardCompareExport(loginUser, summary, bomInfo.getAircraftModel(), bomInfo.getTrack(), bomInfo.getGroupType(), exportDir);
    }

    @Override
    public Long exportStatusStrengthCompare(LoginUser loginUser, BomInfo bomInfo, Path exportDir) {
        List<DetailStatusStrengthChange> changes = bomQueryService.queryStatusStrengthChangeDetailsByBomInfo(bomInfo);
        SummaryStatusStrengthChangeVo summary = JsonUtils.parseObject(bomInfo.getStatusVsStrengthSummary(), SummaryStatusStrengthChangeVo.class);
        BizAssertUtil.notNull(summary, "parse change summary fail");
        summary.setChangeDetailList(MapstructUtils.convert(changes, DetailStatusStrengthChangeVo.class));
        return statusStrengthCompareExport(loginUser, summary, bomInfo.getAircraftModel(), bomInfo.getTrack(), bomInfo.getGroupType(), exportDir);
    }

    @Override
    public void exportBomByTypeAndIds(LoginUser loginUser, String aircraftModel, List<Long> bomIds, BomDetailTypeEnum bomDetailType, HttpServletResponse response) {
        List<BomInfo> bomInfos = bomQueryService.getBomInfoByIds(bomIds);
        BizAssertUtil.isTrue(1 == bomInfos.stream().map(BomInfo::getTrack).distinct().toList().size(), "bomIds not belong to single track");
        Path tempDir = null;
        try {
            try {
                tempDir = Files.createTempDirectory(bomDetailType.getType());
            } catch (IOException ex) {
                throw new BizException("create temp dir error", ex);
            }
            final Path finalTempDir = tempDir;
            switch (bomDetailType) {
                case SBOM:
                    bomInfos.forEach(item -> BizAssertUtil.notNull(item.getStandardBomFileId(), MessageFormat.format("standardBom has not generate for bomId:{0}", item.getId())));
                    bomInfos.forEach(item -> exportStandardBom(loginUser, item, finalTempDir));
                    break;
                case QBOM:
                    bomInfos.forEach(item -> BizAssertUtil.notNull(item.getStandardBomFileId(), MessageFormat.format("strengthBom has not generate for bomId:{0}", item.getId())));
                    bomInfos.forEach(item -> exportStrengthBom(loginUser, item, finalTempDir));
                    break;
                case QBOM_SBOM_CHANGE:
                    bomInfos.forEach(item -> BizAssertUtil.notNull(item.getStandardBomFileId(), MessageFormat.format("strengthBom has not generate for bomId:{0}", item.getId())));
                    bomInfos.forEach(item -> exportStrengthStandardCompare(loginUser, item, finalTempDir));
                    break;
                case MSTS_QBOM_CHANGE:
                    bomInfos.forEach(item -> BizAssertUtil.notNull(item.getStandardBomFileId(), MessageFormat.format("strengthBom has not generate for bomId:{0}", item.getId())));
                    bomInfos.forEach(item -> exportStatusStrengthCompare(loginUser, item, finalTempDir));
                    break;
                default:
                    throw new BizException("not support bom detail type");
            }
            //写入下载
            try {
                File[] files = Objects.requireNonNull(tempDir.toFile().listFiles());
                response.setContentType("application/octet-stream");
                if (files.length > 1) {
                    response.setHeader("Content-Disposition", MessageFormat.format("attachment; filename=\"{0}_{1}.zip\"", aircraftModel, URLEncoder.encode(bomDetailType.getDesc(), StandardCharsets.UTF_8)));
                    try (OutputStream zipOs = response.getOutputStream()) {
                        writeToZip(zipOs, files, null);
                    }
                } else {
                    response.setHeader("Content-Disposition", MessageFormat.format("attachment; filename=\"{0}\"", URLEncoder.encode(files[0].getName(), StandardCharsets.UTF_8)));
                    try (OutputStream os = response.getOutputStream()) {
                        os.write(Files.readAllBytes(files[0].toPath()));
                    }
                }
            } catch (IOException e) {
                throw new BizException("write to zip fail", e);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BizException("write fail", e);
        } finally {
            //删除临时文件夹
            if (null != tempDir) {
                for (File file : tempDir.toFile().listFiles()) {
                    file.delete();
                }
                tempDir.toFile().delete();
            }
        }
    }


    private Long standardBomExport(LoginUser loginUser, List<DetailStandardBomVo> detailStandardBomVoList, String aircraftModel, String track, String groupType, Path exportDir) {
        //导出并上传文件
        String exportFileName = StringUtils.join(Arrays.asList(groupType, "标准装机清单"), "_");
        try {
            File tmpExportFile = File.createTempFile(exportFileName, ".xlsx");
            try (FileOutputStream os = new FileOutputStream(tmpExportFile)) {
                ExcelUtil.exportExcel(detailStandardBomVoList, groupType, DetailStandardBomVo.class, os);
            }
            if (exportDir != null) {
                Path exportFile = exportDir.resolve(exportFileName + ".xlsx");
                FileUtils.copyFile(tmpExportFile, exportFile.toFile());
            }
            BizFileVo uploadResult = bizFileService.upload(loginUser, BizFileTypeEnum.SBOM, tmpExportFile, exportFileName + ".xlsx", track, aircraftModel, groupType, null);
            BizAssertUtil.notNull(uploadResult, MessageFormat.format("upload {0}} file fail", exportFileName));
            return uploadResult.getFileId();
        } catch (Exception e) {
            log.error(MessageFormat.format("export {0} error", exportFileName), e);
            throw new BizException(MessageFormat.format("export {0} error:{1}", exportFileName, e.getMessage()));
        }
    }

    private Long strengthBomExport(LoginUser loginUser, List<DetailStrengthBomVo> strengthBomDetailVos, String aircraftModel, String track, String groupType, Path exportDir) {
        String exportFileName = StringUtils.join(Arrays.asList(groupType, "强度数模清单"), "_");
        try {
            //导出文件
            File tmpExportFile = File.createTempFile(exportFileName, ".xlsx");
            try (FileOutputStream os = new FileOutputStream(tmpExportFile)) {
                DetailStrengthBomVo.exportStrengthBom(os, groupType, strengthBomDetailVos);
            }
            if (exportDir != null) {
                Path exportFile = exportDir.resolve(exportFileName + ".xlsx");
                FileUtils.copyFile(tmpExportFile, exportFile.toFile());
            }
            //上传文件
            BizFileVo uploadResult = bizFileService.upload(loginUser, BizFileTypeEnum.QBOM, tmpExportFile, exportFileName + ".xlsx", track, aircraftModel, groupType, null);
            BizAssertUtil.notNull(uploadResult, MessageFormat.format("upload {0}} file fail", exportFileName));
            return uploadResult.getFileId();
        } catch (Exception e) {
            log.error(MessageFormat.format("export {0} error", exportFileName), e);
            throw new BizException(MessageFormat.format("export {0} error:{1}", exportFileName, e.getMessage()));
        }
    }

    private Long strengthStandardCompareExport(LoginUser loginUser, SummaryStrengthStandardChangeVo compareResult, String aircraftModel, String track, String groupType, Path exportDir) {
        String exportFileName = StringUtils.join(Arrays.asList( groupType, "强度数模清单对比结果(含新增)"), "_");
        String exportFileNameNoAdd = StringUtils.join(Arrays.asList(groupType, "强度数模清单对比结果"), "_");
        File tmpExportFile = null;
        File tmpExportFileNoAdd = null;
        try {
            //导出文件
            tmpExportFile = File.createTempFile(exportFileName, ".xlsx");
            try (FileOutputStream os = new FileOutputStream(tmpExportFile)) {
                //生成含新增的对比
                List<DetailStrengthStandardChangeVo> strengthStandardChangeDetailVos = compareResult.getChangeDetailList();
                DetailStrengthStandardChangeVo.exportStrengthStandardChange(os, groupType, strengthStandardChangeDetailVos);
            }
            tmpExportFileNoAdd = File.createTempFile(exportFileNameNoAdd, ".xlsx");
            try (FileOutputStream os = new FileOutputStream(tmpExportFileNoAdd)) {
                //生成不含新增的对比
                List<DetailStrengthStandardChangeVo> strengthStandardChangeDetailVosNoAdd = compareResult.getChangeDetailList().stream().filter(item -> !StringUtils.equals(ADD.getType(), item.getChangeType())).toList();
                DetailStrengthStandardChangeVo.exportStrengthStandardChange(os, groupType, strengthStandardChangeDetailVosNoAdd);
            }
            if (exportDir != null) {
                Path exportFile = exportDir.resolve(exportFileName + ".xlsx");
                FileUtils.copyFile(tmpExportFile, exportFile.toFile());
                Path exportFileNoAdd = exportDir.resolve(exportFileNameNoAdd + ".xlsx");
                FileUtils.copyFile(tmpExportFileNoAdd, exportFileNoAdd.toFile());
            }
            //写入Zip
            File zipFile = File.createTempFile(exportFileNameNoAdd, ".zip");
            try (FileOutputStream zipFileOs = new FileOutputStream(zipFile)) {
                writeToZip(zipFileOs, new File[]{tmpExportFile, tmpExportFileNoAdd}, new String[]{exportFileName + ".xlsx", exportFileNameNoAdd + ".xlsx"});
            }

            //上传Zip
            BizFileVo uploadResult = bizFileService.upload(loginUser, BizFileTypeEnum.SQ_CHANGE, zipFile, exportFileNameNoAdd + ".zip", track, aircraftModel, groupType, null);
            BizAssertUtil.notNull(uploadResult, MessageFormat.format("upload {0} file fail", exportFileName));
            return uploadResult.getFileId();
        } catch (Exception e) {
            log.error(MessageFormat.format("export {0} error", exportFileName), e);
            throw new BizException(MessageFormat.format("export {0} error:{1}", exportFileName, e.getMessage()));
        } finally {
            if (null != tmpExportFile && tmpExportFile.exists()) {
                tmpExportFile.delete();
            }
            if (null != tmpExportFileNoAdd && tmpExportFileNoAdd.exists()) {
                tmpExportFileNoAdd.delete();
            }
        }
    }

    private Long statusStrengthCompareExport(LoginUser loginUser, SummaryStatusStrengthChangeVo compareResult, String aircraftModel, String track, String groupType, Path exportDir) {
        String exportExcelName = StringUtils.join(Arrays.asList( groupType, "模型状态清单对比结果"), "_");
        String exportLogName = StringUtils.join(Arrays.asList( groupType, "UpdateWorkScope"), "_");
        File tmpExportExcelFile = null;
        File tmpExportLogFile = null;
        try {
            //导出文件
            tmpExportExcelFile = File.createTempFile(exportExcelName, ".xlsx");
            try (FileOutputStream os = new FileOutputStream(tmpExportExcelFile)) {
                //生成对比Excel
                DetailStatusStrengthChangeVo.exportStatusStrengthChange(os, groupType, compareResult.getChangeDetailList(), compareResult.getChangeCountMap());
            }
            tmpExportLogFile = File.createTempFile(exportLogName, ".log");
            try (FileOutputStream os = new FileOutputStream(tmpExportLogFile)) {
                //生成对比日志
                compareResult.exportSummaryLog(os);
            }
            if (exportDir != null) {
                Path exportFile = exportDir.resolve(exportExcelName + ".xlsx");
                FileUtils.copyFile(tmpExportExcelFile, exportFile.toFile());
                Path exportFileLog = exportDir.resolve(exportLogName + ".log");
                FileUtils.copyFile(tmpExportLogFile, exportFileLog.toFile());
            }
            //写入Zip
            File zipFile = File.createTempFile(exportExcelName, ".zip");
            try (FileOutputStream zipFileOs = new FileOutputStream(zipFile)) {
                writeToZip(zipFileOs, new File[]{tmpExportExcelFile, tmpExportLogFile}, new String[]{exportExcelName + ".xlsx", exportLogName + ".log"});
            }

            //上传Zip
            BizFileVo uploadResult = bizFileService.upload(loginUser, BizFileTypeEnum.QM_CHANGE, zipFile, exportExcelName + ".zip", track, aircraftModel, groupType, null);
            BizAssertUtil.notNull(uploadResult, MessageFormat.format("upload {0} file fail", exportExcelName));
            return uploadResult.getFileId();
        } catch (Exception e) {
            log.error(MessageFormat.format("export {0} error", exportExcelName), e);
            throw new BizException(MessageFormat.format("export {0} error:{1}", exportExcelName, e.getMessage()));
        } finally {
            if (null != tmpExportExcelFile && tmpExportExcelFile.exists()) {
                tmpExportExcelFile.delete();
            }
            if (null != tmpExportLogFile && tmpExportLogFile.exists()) {
                tmpExportLogFile.delete();
            }
        }
    }

    private void writeToZip(OutputStream outputZip, File[] files, String[] fileNames) {
        try (ZipOutputStream zip = new ZipOutputStream(outputZip)) {
            for (int i = 0; i < files.length; i++) {
                zip.putNextEntry(new ZipEntry(fileNames == null ? files[i].getName() : fileNames[i]));
                zip.write(Files.readAllBytes(files[i].toPath()));
                zip.closeEntry();
            }
        } catch (IOException e) {
            throw new BizException("export to zip fail", e);
        }
    }
}
